package com.youlai.boot.factory.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.youlai.boot.core.security.util.SecurityUtils;
import com.youlai.boot.factory.enums.AuditStatusEnum;
import com.youlai.boot.factory.enums.SettlementStatusEnum;
import com.youlai.boot.factory.enums.WorkReportTypeEnum;
import com.youlai.boot.factory.mapper.*;
import com.youlai.boot.factory.model.entity.*;
import com.youlai.boot.factory.model.entity.Process;
import com.youlai.boot.factory.model.form.ProcessPackageDTO;
import com.youlai.boot.factory.model.form.WorkReportPackageForm;
import com.youlai.boot.factory.model.vo.MonthlySalaryVO;
import com.youlai.boot.factory.model.vo.WorkReportDetailVO;
import com.youlai.boot.factory.model.vo.WorkOrderProcessDetailVO;
import com.youlai.boot.factory.model.dto.DuplicateWorkReportDTO;
import com.youlai.boot.system.model.entity.User;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.boot.factory.service.WorkReportService;
import com.youlai.boot.factory.model.form.WorkReportForm;
import com.youlai.boot.factory.model.query.WorkReportQuery;
import com.youlai.boot.factory.model.vo.WorkReportVO;
import com.youlai.boot.factory.converter.WorkReportConverter;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.youlai.boot.common.exception.BusinessException;
import org.springframework.transaction.annotation.Transactional;

/**
 * 报工单服务实现类
 *
 * @author youlaitech
 * @since 2025-10-13 13:30
 */
@Service
@RequiredArgsConstructor
public class WorkReportServiceImpl extends ServiceImpl<WorkReportMapper, WorkReport> implements WorkReportService {

    private final WorkReportConverter workReportConverter;

    /**
     * 获取报工单分页列表
     *
     * @param queryParams 查询参数
     * @return {@link IPage<WorkReportVO>} 报工单分页列表
     */
    @Override
    public IPage<WorkReportVO> getWorkReportPage(WorkReportQuery queryParams) {
        Page<WorkReportVO> pageVO = this.baseMapper.getWorkReportPage(
                new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),
                queryParams
        );
        return pageVO;
    }

    /**
     * 获取我的报工单分页列表
     *
     * @param queryParams 查询参数
     * @return {@link IPage<WorkReportVO>} 报工单分页列表
     */
    @Override
    public IPage<WorkReportVO> getMyWorkReportPage(WorkReportQuery queryParams) {
        //将当前用户的id作为查询条件
        queryParams.setWorkerId(SecurityUtils.getUserId());
        Page<WorkReportVO> pageVO = this.baseMapper.getMyWorkReportPage(
                new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),
                queryParams
        );
        return pageVO;
    }


    /**
     * 获取报工单表单数据
     *
     * @param id 报工单ID
     * @return 报工单表单数据
     */
    @Override
    public WorkReportForm getWorkReportFormData(Long id) {
        WorkReport entity = this.getById(id);
        return workReportConverter.toForm(entity);
    }


    @Autowired
    private StyleProcessMapper styleProcessMapper;

    @Autowired
    private WorkReportPackageMapper workReportPackageMapper;

    @Autowired
    private GarmentStyleMapper garmentStyleMapper;

    @Autowired
    private WorkReportMapper workReportMapper;

    @Autowired
    private com.youlai.boot.system.mapper.UserMapper userMapper;

    @Autowired
    private ProcessMapper processMapper;

    /**
     * 获取报工单详情
     *
     * @param id 报工单ID
     * @return 报工单表单数据
     */
    @Override
    public WorkReportDetailVO getWorkReportDetail(Long id) {
        //获取报工单详情 分两种情况
        //一：分包类型的工单 1.获取报工单关联的包列表 2.获取报工单关联的工序列表 由此前端可以渲染工序列表 每个工序中的分包列表
        //二：上数类型的工单：1.获取报工单关联的工序列表
        WorkReportQuery workReportQuery = new WorkReportQuery();
        workReportQuery.setId(id);
        WorkReportDetailVO workReportDetailVo = workReportMapper.getMyWorkReportDetail(workReportQuery);

        // 获取报工人信息
        if (workReportDetailVo.getWorkerId() != null) {
            User worker = userMapper.selectById(workReportDetailVo.getWorkerId());
            if (worker != null) {
                workReportDetailVo.setWorkerName(worker.getUsername());
                workReportDetailVo.setWorkerNickname(worker.getNickname());
            }
        }

        // 获取工序列表
        if (workReportDetailVo.getProcessIds() != null && !workReportDetailVo.getProcessIds().isEmpty()) {
            List<Long> processIdList = Arrays.stream(workReportDetailVo.getProcessIds().split(","))
                    .map(String::trim)
                    .filter(s -> !s.isEmpty())
                    .map(Long::parseLong)
                    .collect(Collectors.toList());

            List<StyleProcess> styleProcessList = styleProcessMapper.selectList(
                    new QueryWrapper<StyleProcess>()
                            .in("id", processIdList)
            );
            workReportDetailVo.setStyleProcessList(styleProcessList);
        }

        // 如果是分包类型，获取分包数据并按工序分组
        if (workReportDetailVo.getReportType().equals(WorkReportTypeEnum.SUB_CONTRACTING.getValue())) {
            List<WorkReportPackage> workReportPackageList = workReportPackageMapper.selectList(
                    new QueryWrapper<WorkReportPackage>().eq("work_report_id", id)
            );
            workReportDetailVo.setWorkReportPackageList(workReportPackageList);

            // 获取报工工序明细
            List<WorkOrderProcess> workOrderProcessList = workOrderProcessMapper.selectList(
                    new QueryWrapper<WorkOrderProcess>().eq("work_report_id", id)
            );

            // 构建工序明细VO列表
            List<WorkOrderProcessDetailVO> workOrderProcessDetailList = workOrderProcessList.stream()
                    .map(workOrderProcess -> {
                        WorkOrderProcessDetailVO detailVO = new WorkOrderProcessDetailVO();
                        detailVO.setId(workOrderProcess.getId());
                        detailVO.setWorkReportId(workOrderProcess.getWorkReportId());
                        detailVO.setProcessId(workOrderProcess.getProcessId());
                        detailVO.setWorkReportQuantity(workOrderProcess.getWorkReportQuantity());
                        detailVO.setUnitPrice(workOrderProcess.getUnitPrice());

                        // 获取工序名称
                        StyleProcess styleProcess = styleProcessMapper.selectById(workOrderProcess.getProcessId());
                        if (styleProcess != null) {
                            // 查询工序基础信息
                            Process process = processMapper.selectById(styleProcess.getProcessId());
                            if (process != null) {
                                detailVO.setProcessName(process.getProcessName());
                            }
                        }

                        // 获取该工序下的分包列表
                        List<WorkReportPackage> packageList = workReportPackageList.stream()
                                .filter(pkg -> pkg.getProcessId().equals(workOrderProcess.getProcessId()))
                                .collect(Collectors.toList());
                        detailVO.setPackageList(packageList);

                        return detailVO;
                    })
                    .collect(Collectors.toList());

            workReportDetailVo.setWorkOrderProcessDetailList(workOrderProcessDetailList);
        }

        return workReportDetailVo;
    }


    /**
     * 新增报工单
     *
     * @param formData 报工单表单对象
     * @return 是否新增成功
     */
    @Override
    public boolean saveWorkReport(WorkReportForm formData) {
        WorkReport entity = workReportConverter.toEntity(formData);
        return this.save(entity);
    }


    @Autowired
    private CuttingBedDetailsMapper cuttingBedDetailsMapper;

    @Autowired
    private WorkOrderProcessMapper workOrderProcessMapper;

    /**
     * 报工提交
     *
     * @param formData 报工单表单对象
     * @return 是否新增成功
     */
    @Transactional
    @Override
    public boolean submitWorkReport(WorkReportForm formData) {
        if (formData.getReportType().equals(WorkReportTypeEnum.SUB_CONTRACTING.getValue())) {
            return submitWorkReportSubPackage(formData);
        } else if (formData.getReportType().equals(WorkReportTypeEnum.REPORTING.getValue())) {
            return submitWorkReportUpNum(formData);
        } else {
            return false;
        }
    }


    // 分包报工
    @Transactional
    public boolean submitWorkReportSubPackage(WorkReportForm formData) {
        // 校验：检查工序数组和包ID列表
        validateProcessPackageList(formData);

        // 校验：检查报工数量不超过裁床分包总数量
        validateWorkReportQuantity(formData);

        // 校验：检查是否存在重复报工
        validateDuplicateWorkReport(formData);

        // 一、报工单表新增
        WorkReport entity = workReportConverter.toEntity(formData);

        //1.计算总工资 1.1根据processPackageList下面遍历的工序ids获取款下面的工序列表 1.2将获取的工序信息放到processPackageList中 1.3 根据processPackageList的packageIds，来获取包的详细信息列表 将获取的包信息信息放到processPackageList中 1.4 计算总工资
        BigDecimal totalSalary = BigDecimal.ZERO;

        // 1.1 根据processPackageList下面遍历的工序ids获取款下面的工序列表
        List<Long> processIds = formData.getProcessPackageList().stream()
                .map(ProcessPackageDTO::getProcessId)
                .distinct()
                .collect(Collectors.toList());

        List<StyleProcess> styleProcessList = styleProcessMapper.selectList(
                new QueryWrapper<StyleProcess>().in("id", processIds)
        );

        // 创建工序ID到工序信息的映射，便于快速查找
        Map<Long, StyleProcess> processMap = styleProcessList.stream()
                .collect(Collectors.toMap(StyleProcess::getId, styleProcess -> styleProcess));

        // 在循环外先批量获取所有包信息
        List<Long> allPackageIds = formData.getProcessPackageList().stream()
                .flatMap(processPackage -> processPackage.getPackageIds().stream())
                .distinct()
                .collect(Collectors.toList());

        List<CuttingBedDetails> allPackageList = Collections.emptyList();
        if (!allPackageIds.isEmpty()) {
            allPackageList = cuttingBedDetailsMapper.selectList(
                    new QueryWrapper<CuttingBedDetails>()
                            .eq("cutting_bed_id", formData.getCuttingBedId())
                            .in("id", allPackageIds)
            );
        }

        // 创建包ID到包信息的映射
        Map<Long, CuttingBedDetails> packageMap = allPackageList.stream()
                .collect(Collectors.toMap(CuttingBedDetails::getId, pkg -> pkg));


        // 1.4 计算总工资和总数量
        int grandTotalQuantity = 0;
        for (ProcessPackageDTO processPackage : formData.getProcessPackageList()) {
            Long processId = processPackage.getProcessId();
            StyleProcess styleProcess = processMap.get(processId);

            if (styleProcess != null) {
                // 从映射中获取该工序下所有分包的数量总和
                int totalQuantity = processPackage.getPackageIds().stream()
                        .map(packageMap::get)
                        .filter(Objects::nonNull)
                        .mapToInt(CuttingBedDetails::getCuttingQuantity)
                        .sum();

                // 累加总数量
                grandTotalQuantity += totalQuantity;

                // 工序单价 × 工序总数量 = 该工序工资
                BigDecimal processSalary = styleProcess.getCustomPrice()
                        .multiply(BigDecimal.valueOf(totalQuantity));

                // 累加到总工资
                totalSalary = totalSalary.add(processSalary);
            }
        }

        //设置报工单的基础信息 报工人 报工数量 报工金额 待审核状态
        entity.setReportDate(LocalDateTime.now());
        entity.setWorkerId(SecurityUtils.getUserId());
        entity.setAuditStatus(AuditStatusEnum.PENDING.getValue());
        entity.setReportSalary(totalSalary);
        entity.setTotalQuantity(grandTotalQuantity);


        boolean saved = this.save(entity);
        if (!saved) {
            return false;
        }

        Long workReportId = entity.getId();

        // 二、报工工序表新增 工序下的分包新增
        List<WorkOrderProcess> workOrderProcessList = formData.getProcessPackageList().stream()
                .map(processPackage -> {
                    // 从 processMap 中获取该工序的信息
                    StyleProcess styleProcess = processMap.get(processPackage.getProcessId());

                    WorkOrderProcess workOrderProcess = new WorkOrderProcess();
                    workOrderProcess.setWorkReportId(workReportId);
                    workOrderProcess.setProcessId(processPackage.getProcessId());
                    // 计算该工序的报工数量
                    int totalQuantity = processPackage.getPackageIds().stream()
                            .map(packageMap::get)
                            .filter(Objects::nonNull)
                            .mapToInt(CuttingBedDetails::getCuttingQuantity)
                            .sum();
                    workOrderProcess.setWorkReportQuantity(totalQuantity);
                    // 设置该工序的单价
                    workOrderProcess.setUnitPrice(styleProcess.getCustomPrice());
                    workOrderProcess.setCreateBy(SecurityUtils.getUsername());
                    workOrderProcess.setCreateTime(LocalDateTime.now());
                    return workOrderProcess;
                })
                .collect(Collectors.toList());

        int result1 = workOrderProcessMapper.batchSave(workOrderProcessList);

        // 三、构建并保存分包子项
        List<WorkReportPackage> workReportPackages = formData.getProcessPackageList().stream()
                .flatMap(processPackage -> processPackage.getPackageIds().stream()
                        .map(packageMap::get)
                        .filter(Objects::nonNull)
                        .map(cuttingBedDetails -> {
                            WorkReportPackage workReportPackage = new WorkReportPackage();
                            workReportPackage.setWorkReportId(workReportId);
                            workReportPackage.setProcessId(processPackage.getProcessId());
                            workReportPackage.setCuttingPackageId(cuttingBedDetails.getId());
                            workReportPackage.setCuttingBedId(formData.getCuttingBedId());
                            workReportPackage.setCreateBy(SecurityUtils.getUsername());
                            workReportPackage.setCreateTime(LocalDateTime.now());
                            return workReportPackage;
                        }))
                .collect(Collectors.toList());

        int batchSaved = 0;
        if (!workReportPackages.isEmpty()) {
            batchSaved = workReportPackageMapper.batchSave(workReportPackages);
        }

        return workReportId > 0 && result1 > 0 && batchSaved >= 0;
    }


    //上数报工
    @Transactional
    public boolean submitWorkReportUpNum(WorkReportForm formData) {
        Assert.isTrue(formData.getStyleId() != null, "款ID不能为空");
        Assert.isTrue(formData.getCuttingBedId() != null, "裁床ID不能为空");
        Assert.isTrue(formData.getProcessPackageList() != null && !formData.getProcessPackageList().isEmpty(), "工序列表不能为空");

        // 校验每个工序的报工数量
        int totalQuantity = 0;
        for (ProcessPackageDTO processPackage : formData.getProcessPackageList()) {
            if (processPackage.getProcessId() == null) {
                throw new BusinessException("工序ID不能为空");
            }
            if (processPackage.getWorkReportQuantity() == null || processPackage.getWorkReportQuantity() <= 0) {
                throw new BusinessException(String.format("工序ID【%d】的报工数量不能为空且必须大于0", processPackage.getProcessId()));
            }
            totalQuantity += processPackage.getWorkReportQuantity();
        }

        // 校验：检查报工数量不超过裁床分包总数量
        validateWorkReportQuantity(formData);

        // 一、新增报工单
        WorkReport entity = workReportConverter.toEntity(formData);

        // 解析工序ID
        List<Long> processIdList = formData.getProcessPackageList().stream()
                .map(ProcessPackageDTO::getProcessId)
                .distinct()
                .collect(Collectors.toList());

        List<StyleProcess> styleProcessList = styleProcessMapper.selectList(
                new QueryWrapper<StyleProcess>().in("id", processIdList)
        );

        // 创建工序ID到工序信息的映射
        Map<Long, StyleProcess> processMap = styleProcessList.stream()
                .collect(Collectors.toMap(StyleProcess::getId, styleProcess -> styleProcess));

        // 计算总工资：每个工序的单价 × 该工序的报工数量，然后累加
        BigDecimal totalSalary = BigDecimal.ZERO;
        for (ProcessPackageDTO processPackage : formData.getProcessPackageList()) {
            StyleProcess styleProcess = processMap.get(processPackage.getProcessId());
            if (styleProcess != null) {
                BigDecimal processSalary = styleProcess.getCustomPrice()
                        .multiply(BigDecimal.valueOf(processPackage.getWorkReportQuantity()));
                totalSalary = totalSalary.add(processSalary);
            }
        }

        entity.setReportDate(LocalDateTime.now());
        entity.setWorkerId(SecurityUtils.getUserId());
        entity.setTotalQuantity(totalQuantity);
        entity.setAuditStatus(AuditStatusEnum.PENDING.getValue());
        entity.setReportSalary(totalSalary);

        // 保存报工单
        boolean saved = this.save(entity);
        if (!saved) {
            return false;
        }

        Long workReportId = entity.getId();

        // 二、批量新增报工工序明细
        List<WorkOrderProcess> workOrderProcessList = formData.getProcessPackageList().stream()
                .map(processPackage -> {
                    StyleProcess styleProcess = processMap.get(processPackage.getProcessId());
                    WorkOrderProcess workOrderProcess = new WorkOrderProcess();
                    workOrderProcess.setWorkReportId(workReportId);
                    workOrderProcess.setProcessId(processPackage.getProcessId());
                    workOrderProcess.setWorkReportQuantity(processPackage.getWorkReportQuantity());
                    workOrderProcess.setUnitPrice(styleProcess.getCustomPrice());
                    workOrderProcess.setCreateBy(SecurityUtils.getUsername());
                    workOrderProcess.setCreateTime(LocalDateTime.now());
                    return workOrderProcess;
                })
                .collect(Collectors.toList());

        int result = workOrderProcessMapper.batchSave(workOrderProcessList);

        return workReportId > 0 && result > 0;
    }


    /**
     * 更新报工单
     *
     * @param id       报工单ID
     * @param formData 报工单表单对象
     * @return 是否修改成功
     */
    @Override
    public boolean updateWorkReport(Long id, WorkReportForm formData) {
        WorkReport entity = workReportConverter.toEntity(formData);
        return this.updateById(entity);
    }

    /**
     * 删除报工单
     *
     * @param ids 报工单ID，多个以英文逗号(,)分割
     * @return 是否删除成功
     */
    @Override
    public boolean deleteWorkReports(String ids) {
        Assert.isTrue(StrUtil.isNotBlank(ids), "删除的报工单数据为空");
        // 逻辑删除
        List<Long> idList = Arrays.stream(ids.split(","))
                .map(Long::parseLong)
                .toList();
        return this.removeByIds(idList);
    }

    /**
     * 校验工序和包列表
     * 检查工序数组是否为空，以及每个工序下的包ID列表是否为空
     *
     * @param formData 报工单表单数据
     */
    private void validateProcessPackageList(WorkReportForm formData) {
        // 校验工序数组不能为空
        if (formData.getProcessPackageList() == null || formData.getProcessPackageList().isEmpty()) {
            throw new BusinessException("工序列表不能为空");
        }

        // 校验每个工序下的包ID列表不能为空
        for (int i = 0; i < formData.getProcessPackageList().size(); i++) {
            ProcessPackageDTO processPackage = formData.getProcessPackageList().get(i);

            if (processPackage.getProcessId() == null) {
                throw new BusinessException(String.format("第%d个工序的工序ID不能为空", i + 1));
            }

            if (processPackage.getPackageIds() == null || processPackage.getPackageIds().isEmpty()) {
                throw new BusinessException(String.format("工序ID【%d】下的包ID列表不能为空", processPackage.getProcessId()));
            }
        }
    }

    /**
     * 校验报工数量不超过裁床分包总数量
     * 对于分包报工：累加所有包的数量总和
     * 对于上数报工：按每个工序分别校验，该工序的报工数量不能超过裁床分包总数量
     *
     * @param formData 报工单表单数据
     */
    private void validateWorkReportQuantity(WorkReportForm formData) {
        // 获取该裁床下的所有分包
        List<CuttingBedDetails> allPackages = cuttingBedDetailsMapper.selectList(
                new QueryWrapper<CuttingBedDetails>().eq("cutting_bed_id", formData.getCuttingBedId())
        );

        // 计算裁床分包总数量
        int totalCuttingQuantity = allPackages.stream()
                .mapToInt(CuttingBedDetails::getCuttingQuantity)
                .sum();

        // 判断报工类型
        if (formData.getReportType().equals(WorkReportTypeEnum.SUB_CONTRACTING.getValue())) {
            // 分包报工：累加所有包的数量
            int totalWorkReportQuantity = 0;
            for (ProcessPackageDTO processPackage : formData.getProcessPackageList()) {
                for (Long packageId : processPackage.getPackageIds()) {
                    CuttingBedDetails pkg = allPackages.stream()
                            .filter(p -> p.getId().equals(packageId))
                            .findFirst()
                            .orElse(null);
                    if (pkg != null) {
                        totalWorkReportQuantity += pkg.getCuttingQuantity();
                    }
                }
            }

            if (totalWorkReportQuantity > totalCuttingQuantity) {
                throw new BusinessException(
                        String.format("报工数量【%d】超过裁床分包总数量【%d】",
                                totalWorkReportQuantity, totalCuttingQuantity)
                );
            }
        } else if (formData.getReportType().equals(WorkReportTypeEnum.REPORTING.getValue())) {
            // 上数报工：按每个工序分别校验
            for (ProcessPackageDTO processPackage : formData.getProcessPackageList()) {
                int currentWorkReportQuantity = processPackage.getWorkReportQuantity() != null
                        ? processPackage.getWorkReportQuantity() : 0;

                // 查询该工序在该裁床下的历史报工数量
                List<WorkOrderProcess> historyWorkReports = workOrderProcessMapper.selectList(
                        new QueryWrapper<WorkOrderProcess>()
                                .eq("process_id", processPackage.getProcessId())
                );

                // 查找历史报工单，筛选出属于该裁床的报工
                int historyQuantity = 0;
                for (WorkOrderProcess historyReport : historyWorkReports) {
                    WorkReport historyWorkReport = this.getById(historyReport.getWorkReportId());
                    if (historyWorkReport != null && historyWorkReport.getCuttingBedId().equals(formData.getCuttingBedId())) {
                        historyQuantity += historyReport.getWorkReportQuantity();
                    }
                }

                // 计算总报工数量 = 历史报工数量 + 本次报工数量
                int totalWorkReportQuantity = historyQuantity + currentWorkReportQuantity;

                if (totalWorkReportQuantity > totalCuttingQuantity) {
                    // 获取工序名称
                    StyleProcess styleProcess = styleProcessMapper.selectById(processPackage.getProcessId());
                    String processName = styleProcess != null ? getProcessName(styleProcess.getProcessId()) : "未知工序";

                    throw new BusinessException(
                            String.format("工序【%s】的报工数量【%d】（历史【%d】+本次【%d】）超过裁床分包总数量【%d】",
                                    processName, totalWorkReportQuantity, historyQuantity, currentWorkReportQuantity, totalCuttingQuantity)
                    );
                }
            }
        }
    }

    /**
     * 校验重复报工
     * 检查同一个裁床ID下，同一个工序的同一个包是否已经报工过
     *
     * @param formData 报工单表单数据
     */
    private void validateDuplicateWorkReport(WorkReportForm formData) {
        if (formData.getProcessPackageList() == null || formData.getProcessPackageList().isEmpty()) {
            return;
        }

        // 查询已存在的报工记录
        for (ProcessPackageDTO processPackage : formData.getProcessPackageList()) {
            Long processId = processPackage.getProcessId();

            // 获取工序名称
            StyleProcess styleProcess = styleProcessMapper.selectById(processId);
            String processName = styleProcess != null ? getProcessName(styleProcess.getProcessId()) : "未知工序";

            // 检查该工序下的每个包是否已经报工过
            for (Long packageId : processPackage.getPackageIds()) {
                // 查询是否存在相同裁床ID、相同工序ID、相同包ID的报工记录
                List<WorkReportPackage> existingReports = workReportPackageMapper.selectList(
                        new QueryWrapper<WorkReportPackage>()
                                .eq("cutting_bed_id", formData.getCuttingBedId())
                                .eq("process_id", processId)
                                .eq("cutting_package_id", packageId)
                );

                if (!existingReports.isEmpty()) {
                    // 获取第一个重复的记录
                    WorkReportPackage duplicateReport = existingReports.get(0);

                    // 查询报工单信息获取报工时间和报工人
                    WorkReport workReport = this.getById(duplicateReport.getWorkReportId());

                    // 获取包信息
                    CuttingBedDetails packageInfo = cuttingBedDetailsMapper.selectById(packageId);
                    String packageNumber = packageInfo != null ? packageInfo.getPackageNumber() : String.valueOf(packageId);

                    // 获取报工人信息
                    String workerName = "未知";
                    if (workReport != null && workReport.getWorkerId() != null) {
                        User worker = userMapper.selectById(workReport.getWorkerId());
                        if (worker != null) {
                            workerName = worker.getNickname() != null ? worker.getNickname() : worker.getUsername();
                        }
                    }

                    // 构建DTO对象，包含详细信息
                    DuplicateWorkReportDTO duplicateInfo = DuplicateWorkReportDTO.builder()
                            .packageNumber(packageNumber)
                            .processName(processName)
                            .reportDate(workReport != null ? workReport.getReportDate() : null)
                            .workerName(workerName)
                            .workReportId(duplicateReport.getWorkReportId())
                            .build();

                    // 构建简短的错误消息
                    String errorMessage = String.format("包号【%s】在工序【%s】下已经报工过", packageNumber, processName);

                    // 抛出异常，携带详细数据
                    throw new BusinessException(errorMessage, duplicateInfo);
                }
            }
        }
    }

    /**
     * 获取工序名称
     */
    private String getProcessName(Long processId) {
        Process process = processMapper.selectById(processId);
        return process != null ? process.getProcessName() : "未知工序";
    }

    /**
     * 审核通过报工单
     *
     * @param id 报工单ID
     * @return 是否审核成功
     */
    @Override
    public boolean approveWorkReport(Long id) {
        WorkReport workReport = this.getById(id);
        Assert.notNull(workReport, "报工单不存在");

        workReport.setAuditStatus(AuditStatusEnum.APPROVED.getValue()); // 审核通过
        workReport.setUpdateBy(SecurityUtils.getUsername());
        workReport.setUpdateTime(LocalDateTime.now());

        return this.updateById(workReport);
    }

    /**
     * 批量审核通过报工单
     *
     * @param ids 报工单ID列表
     * @return 是否审核成功
     */
    @Override
    @Transactional
    public boolean batchApproveWorkReports(List<Long> ids) {
        Assert.notEmpty(ids, "报工单ID列表不能为空");

        List<WorkReport> workReports = this.listByIds(ids);
        Assert.notEmpty(workReports, "未找到对应的报工单");

        String currentUser = SecurityUtils.getUsername();
        LocalDateTime now = LocalDateTime.now();

        workReports.forEach(workReport -> {
            workReport.setAuditStatus(AuditStatusEnum.APPROVED.getValue()); // 审核通过
            workReport.setUpdateBy(currentUser);
            workReport.setUpdateTime(now);
        });

        return this.updateBatchById(workReports);
    }

    /**
     * 驳回报工单
     *
     * @param id 报工单ID
     * @param rejectReason 驳回原因
     * @return 是否驳回成功
     */
    @Override
    public boolean rejectWorkReport(Long id, String rejectReason) {
        Assert.isTrue(StrUtil.isNotBlank(rejectReason), "驳回原因不能为空");

        WorkReport workReport = this.getById(id);
        Assert.notNull(workReport, "报工单不存在");

        workReport.setAuditStatus(AuditStatusEnum.REJECTED.getValue()); // 审核拒绝
        workReport.setRejectReason(rejectReason);
        workReport.setUpdateBy(SecurityUtils.getUsername());
        workReport.setUpdateTime(LocalDateTime.now());

        return this.updateById(workReport);
    }

    /**
     * 批量驳回报工单
     *
     * @param ids 报工单ID列表
     * @param rejectReason 驳回原因
     * @return 是否驳回成功
     */
    @Override
    @Transactional
    public boolean batchRejectWorkReports(List<Long> ids, String rejectReason) {
        Assert.notEmpty(ids, "报工单ID列表不能为空");
        Assert.isTrue(StrUtil.isNotBlank(rejectReason), "驳回原因不能为空");

        List<WorkReport> workReports = this.listByIds(ids);
        Assert.notEmpty(workReports, "未找到对应的报工单");

        String currentUser = SecurityUtils.getUsername();
        LocalDateTime now = LocalDateTime.now();

        workReports.forEach(workReport -> {
            workReport.setAuditStatus(AuditStatusEnum.REJECTED.getValue()); // 审核拒绝
            workReport.setRejectReason(rejectReason);
            workReport.setUpdateBy(currentUser);
            workReport.setUpdateTime(now);
        });

        return this.updateBatchById(workReports);
    }

    /**
     * 单个报工单结算
     *
     * @param id 报工单ID
     * @return 是否结算成功
     */
    @Override
    public boolean settleWorkReport(Long id) {
        WorkReport workReport = this.getById(id);
        Assert.notNull(workReport, "报工单不存在");

        workReport.setSettlementStatus(Integer.parseInt(SettlementStatusEnum.SETTLED.getValue())); // 已结算
        workReport.setUpdateBy(SecurityUtils.getUsername());
        workReport.setUpdateTime(LocalDateTime.now());

        return this.updateById(workReport);
    }

    /**
     * 批量结算报工单
     *
     * @param ids 报工单ID列表
     * @return 是否结算成功
     */
    @Override
    @Transactional
    public boolean batchSettleWorkReports(List<Long> ids) {
        Assert.notEmpty(ids, "报工单ID列表不能为空");

        List<WorkReport> workReports = this.listByIds(ids);
        Assert.notEmpty(workReports, "未找到对应的报工单");

        String currentUser = SecurityUtils.getUsername();
        LocalDateTime now = LocalDateTime.now();

        workReports.forEach(workReport -> {
            workReport.setSettlementStatus(Integer.parseInt(SettlementStatusEnum.SETTLED.getValue())); // 已结算
            workReport.setUpdateBy(currentUser);
            workReport.setUpdateTime(now);
        });

        return this.updateBatchById(workReports);
    }

    /**
     * 获取当前用户指定月份的工资信息
     *
     * @param month 月份（格式：yyyy-MM）
     * @return 月度工资信息
     */
    @Override
    public MonthlySalaryVO getMonthlySalary(String month) {
        // 1. 验证月份格式
        Assert.notBlank(month, "月份不能为空");
        
        try {
            YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyy-MM"));
        } catch (Exception e) {
            throw new BusinessException("月份格式错误，正确格式为：yyyy-MM");
        }

        // 2. 获取当前用户ID
        Long currentUserId = SecurityUtils.getUserId();

        // 3. 构建查询条件：当前用户、指定月份、已结算
        String startDate = month + "-01 00:00:00";
        YearMonth yearMonth = YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyy-MM"));
        int lastDay = yearMonth.lengthOfMonth();
        String endDate = month + "-" + lastDay + " 23:59:59";

        QueryWrapper<WorkReport> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("worker_id", currentUserId)
                .eq("settlement_status", SettlementStatusEnum.SETTLED.getValue()) // 已结算
                .between("report_date", startDate, endDate)
                .orderByDesc("report_date");

        // 4. 查询已结算的报工单
        List<WorkReport> workReports = this.list(queryWrapper);

        // 5. 计算工资总额
        BigDecimal totalSalary = workReports.stream()
                .map(WorkReport::getReportSalary)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 6. 转换为VO
        WorkReportQuery query = new WorkReportQuery();
        query.setWorkerId(currentUserId);
        List<WorkReportVO> workReportVOList = workReports.stream()
                .map(workReport -> {
                    WorkReportVO vo = new WorkReportVO();
                    vo.setId(workReport.getId());
                    vo.setStyleId(workReport.getStyleId());
                    vo.setWorkerId(workReport.getWorkerId());
                    vo.setCuttingBedId(workReport.getCuttingBedId());
                    vo.setProcessIds(workReport.getProcessIds());
                    vo.setReportSalary(workReport.getReportSalary());
                    vo.setTotalQuantity(workReport.getTotalQuantity());
                    vo.setReportDate(workReport.getReportDate());
                    vo.setStatus(workReport.getStatus());
                    vo.setCreateBy(workReport.getCreateBy());
                    vo.setCreateTime(workReport.getCreateTime());
                    vo.setUpdateBy(workReport.getUpdateBy());
                    vo.setUpdateTime(workReport.getUpdateTime());
                    vo.setReportType(workReport.getReportType());
                    return vo;
                })
                .collect(Collectors.toList());

        // 7. 构建返回结果
        MonthlySalaryVO result = new MonthlySalaryVO();
        result.setMonth(month);
        result.setTotalSalary(totalSalary);
        result.setSettledReportCount(workReports.size());
        result.setWorkReportList(workReportVOList);

        return result;
    }

    /**
     * 获取所有员工指定月份的工资统计列表（老板核对工资用）
     *
     * @param month 月份（格式：yyyy-MM）
     * @return 员工工资统计列表
     */
    @Override
    public List<com.youlai.boot.factory.model.vo.EmployeeSalaryVO> getEmployeeSalaryList(String month) {
        Assert.notBlank(month, "月份不能为空");
        
        try {
            YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyy-MM"));
        } catch (Exception e) {
            throw new BusinessException("月份格式错误，正确格式为：yyyy-MM");
        }
        
        return this.baseMapper.getEmployeeSalaryList(month);
    }

    /**
     * 获取指定员工某月已审核通过的报工单分页列表
     *
     * @param queryParams 查询参数（需包含workerId和settlementMonth）
     * @return 报工单分页列表
     */
    @Override
    public IPage<WorkReportVO> getEmployeeMonthWorkReportPage(WorkReportQuery queryParams) {
        Assert.notNull(queryParams.getWorkerId(), "员工ID不能为空");
        Assert.notBlank(queryParams.getSettlementMonth(), "月份不能为空");
        
        // 设置审核状态为已审核通过
        queryParams.setAuditStatus(AuditStatusEnum.APPROVED.getValue());
        
        Page<WorkReportVO> pageVO = this.baseMapper.getWorkReportPage(
                new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),
                queryParams
        );
        return pageVO;
    }

    /**
     * 获取所有员工的工资发放状态列表
     *
     * @param month 月份（格式：yyyy-MM）
     * @return 员工工资发放状态列表
     */
    @Override
    public List<com.youlai.boot.factory.model.vo.EmployeePaymentStatusVO> getEmployeePaymentStatusList(String month) {
        Assert.notBlank(month, "月份不能为空");
        
        try {
            YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyy-MM"));
        } catch (Exception e) {
            throw new BusinessException("月份格式错误，正确格式为：yyyy-MM");
        }
        
        return this.baseMapper.getEmployeePaymentStatusList(month);
    }

}
